Homebrew Handbook for SNES
Chapter 1: Initializing the SNES
In this chapter we’ll look at startup procedures and a
few helper macros for loading tile data into VRAM. These samples were taken
from the simple DEMOROM demo, available (with full X816 source) on the SNES Developer's Lobby homepage.
Section 1.1: Hardware Reset
Before anything can be done, the SNES must be initialized. While most emulators and the real hardware tend to start up with the same status every time, you should not assume that, for example, RAM will be cleared to zero. You must do it yourself. The SNES registers must also be setup to display a blank screen while you start up your game code. The procedure is fairly straightforward: Write zeros into most registers, with special values going in certain registers. Here’s a code snippit that can be used for most SNES games:
- ;============================================================================
- ; InitializeSNES -- Load palette data into PPU Color RAM
- ;----------------------------------------------------------------------------
- ; In: None
- ;----------------------------------------------------------------------------
- ; Out: None
- ;----------------------------------------------------------------------------
- ; Modifies: A, X, P
- ;----------------------------------------------------------------------------
- InitializeSNES:
- REP #$30
- SEP #$20
- .mem 8
- .index 16
- LDA #$8F
- STA $2100
- LDA #$80
- STA $2115
- LDA #00
- LDX #$2101
- CLRRegLoop:
- STA $0000,X
- INX
- CPX #$210D
- BNE CLRRegLoop
- STA $210E
- STA $210E
- STA $210F
- STA $210F
- STA $2110
- STA $2110
- STA $2111
- STA $2111
- STA $2112
- STA $2112
- STA $2113
- STA $2113
- STA $2114
- STA $2114
- LDX #$2116
- CLRRegLoopB:
- STA $0000,X
- INX
- CPX #$2133
- BNE CLRRegLoopB
- LDX #$4200
- CLRRegLoopC:
- STA $0000,X
- INX
- CPX #$420D
- BNE CLRRegLoopC
- RTS
- ;============================================================================
-
As you can see, the init code does very little real work
and accesses a lot of registers you probably don’t understand the purpose
of. Following this reset, you don’t need to worry about them until you’re ready to
use them. This subroutine, InitializeSNES, should be called immediately by your RESET
handler, like this:
- ; Reset Vector Handler and the beginning of our ROM
- .ORG $8000
- sei ;disable interrupts
- clc ;switch to native mode
- xce
- jsr InitializeSNES
At this point, we’re ready to get into game specific initialization code.
Section 1.2: Game Reset
One of the first things you should do is setup the graphics mode you want. For example, in my demo I used mode 1, which has two 16 color and a 4 color background:
- rep #$30 ;8b mem, 16b X
- sep #$20
- .mem 8
- .index 16
- lda #$01 ;Set video mode 1
- sta $2105
The next step is to load your tiles into VRAM. To do this, I wrote a helper macro called LoadBlockToVRAM. It uses DMA channels to load a block of data into VRAM. It does not necessarily have to be tile data, but that was the intended purpose of the macro. It could also be used to load maps. (See LOADVRAM.ASM for code)
Using this macro to load tiles is pretty simple:
- lda #$01
- sta $210B ;Set BG1's Tile VRAM offset to $1000
- .LoadBlockToVRAM (TiledataBank,Tiledata,$1000,$6400)
;load a 25600 byte tileset from Tiledata into VRAM at $1000
Note that register $210B was set to $01; this tells the PPU to use VRAM address $1000 as the beginning of tile memory for BG1. Also note that VRAM addresses are word addreses rather than byte addresses. Thus, VRAM address $1000 and $4200 are actually 25600 bytes apart, not just the 12800 that you would expect if it were bytes. This "feature" of the SNES VRAM can sometimes make address calculations tricky. A side effect of that is that only $0000 to $7FFF are valid VRAM addresses.
Now that the tiles are in place, we’d better load the palette
into the color registers. I have another helper macro for this, called LoadPalette. Using this macro, you can load many color entries at once.
- .LoadPalette (ColorDataBank, ColorData, FirstColorIndex, NumberOfColors)
Note that ColorData is a label marking a binary block of color data. In the tile load example above, Tiledata is also a label for binary blocks. These blocks are usually external .bin files generated by a tile editor. For example:
- ColorData
- .incbin "tilesclr.bin"
- Tiledata
- .incbin "tiles.bin"
How these includes work varies from assembler to assembler. All of these samples so far were tested using X816, but they are definately not compatable with SNIDE 0.4 or later. SNIDE has its own way of handling symbols and resources like tile and palette data.
Now we’re ready to actually display something on the screen. To do so, we note that BG1’s SC address is set to VRAM address $0000 (the default setting), so that is where we must load our map. In the sample, the tiles are segments of a BMP file in the order they were captured by SNIDE’s BMP to SNES tile converter. So, to display the picture we must set the map for BG1 to an incrementing list of tiles 0 to 1023:
- stz $2116 ;reset VRAM pointer
- stz $2117
- ldx #$0000 ;init counter
- - stx $2118 ;store the tile into VRAM
- inx ;next tile
- cpx #$0800 ;loop
- bne –
This code snippit can be used to make a single unmoving image, perhaps a title screen. However, it does not make the most efficient use of memory, as tiles could be reused to avoid redundancy. Much more intricate code can be used to generate game fields, menus, text, etc. This is up to you. :) Later chapters will cover some of those techniques.
We’re not quite done with our demo: we’ve got to turn on the display and halt the CPU!
- lda #$01 ;enable BG1 to display
- sta $212C
- lda #$0F ;turn on the screen, full brightness
- sta $2100
- - bra - ;begin an infinite loop to halt the program. We could also simply STP here.